// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package os

import (
	"internal/syscall/windows"
	"syscall"
	"unsafe"
)

// Stat返回描述文件的FileInfo结构。
// 如果有错误，则类型为*PathError。
func (file *File) Stat() (FileInfo, error) {
	if file == nil {
		return nil, ErrInvalid
	}

	if file.isdir() {
		// 对于目录
		return Stat(file.dirinfo.path)
	}
	if isWindowsNulName(file.name) {
		return &devNullStat, nil
	}

	ft, err := file.pfd.GetFileType()
	if err != nil {
		return nil, &PathError{Op: "GetFileType", Path: file.name, Err: err}
	}
	switch ft {
	case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR:
		return &fileStat{name: basename(file.name), filetype: ft}, nil
	}

	fs, err := newFileStatFromGetFileInformationByHandle(file.name, file.pfd.Sysfd)
	if err != nil {
		return nil, err
	}
	fs.filetype = ft
	return fs, err
}

// stat实现了文件的stat和Lstat，我不知道有什么更好的方法来实现这一点。
func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
	if len(name) == 0 {
		return nil, &PathError{Op: funcname, Path: name, Err: syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
	}
	if isWindowsNulName(name) {
		return &devNullStat, nil
	}
	namep, err := syscall.UTF16PtrFromString(fixLongPath(name))
	if err != nil {
		return nil, &PathError{Op: funcname, Path: name, Err: err}
	}

	// 首先尝试GetFileAttributesEx，因为它比CreateFile快。
	// 请参阅https:
	var fa syscall.Win32FileAttributeData
	err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
	if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
		// 不是符号链接。
		fs := &fileStat{
			FileAttributes: fa.FileAttributes,
			CreationTime:   fa.CreationTime,
			LastAccessTime: fa.LastAccessTime,
			LastWriteTime:  fa.LastWriteTime,
			FileSizeHigh:   fa.FileSizeHigh,
			FileSizeLow:    fa.FileSizeLow,
		}
		if err := fs.saveInfoFromPath(name); err != nil {
			return nil, err
		}
		return fs, nil
	}
	// GetFileAttributesEx失败，出现错误\u共享\u违反
	// 文件的错误，如c:\pagefile。系统。对此类文件使用FindFirstFile。
	if err == windows.ERROR_SHARING_VIOLATION {
		var fd syscall.Win32finddata
		sh, err := syscall.FindFirstFile(namep, &fd)
		if err != nil {
			return nil, &PathError{Op: "FindFirstFile", Path: name, Err: err}
		}
		syscall.FindClose(sh)
		fs := newFileStatFromWin32finddata(&fd)
		if err := fs.saveInfoFromPath(name); err != nil {
			return nil, err
		}
		return fs, nil
	}

	// 最后使用CreateFile。
	h, err := syscall.CreateFile(namep, 0, 0, nil,
		syscall.OPEN_EXISTING, createFileAttrs, 0)
	if err != nil {
		return nil, &PathError{Op: "CreateFile", Path: name, Err: err}
	}
	defer syscall.CloseHandle(h)

	return newFileStatFromGetFileInformationByHandle(name, h)
}

// statNolog为Windows实现Stat。
func statNolog(name string) (FileInfo, error) {
	return stat("Stat", name, syscall.FILE_FLAG_BACKUP_SEMANTICS)
}

// lstatNolog为Windows实现Lstat。
func lstatNolog(name string) (FileInfo, error) {
	attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
	// 使用文件标志打开重新分析点，否则CreateFile将遵循符号链接。
	// 参见https:
	attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
	return stat("Lstat", name, attrs)
}
